package Singleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author: 蔡翔
 * @Date: 2019/12/4 9:23
 * @Version 1.0
 */
public class Test2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1. 序列化测试方法
        /*HungrySingleton instance = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton_file"));
        oos.writeObject(instance);

        File file = new File("Singleton_file");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

        HungrySingleton newInstance = (HungrySingleton) ois.readObject();

        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);
        */
        /**
         * 结果：
         *
         *  Singleton.HungrySingleton@14ae5a5
         *  Singleton.HungrySingleton@6d03e736
         *  false
         *  两个对象是不相同的，违背了单例模式的初衷，通过序列化和反序列化 拿到了不同的对象，而我们只希望拿到同一个对象。
         *  所有要在 HungrySingleton类中 加入 readResolve方法。
         **/


/*

        //2. 防止反射攻击
        // 因为构造器是私有的 无法被new出来；；但是：我们能不能通过反射把构造器的权限打开，然后去获取这个对象。
        Class objectClass = HungrySingleton.class;

        //Constructor 是反射里边的。
        Constructor constructor = objectClass.getDeclaredConstructor();
        //不加下面代码会报错：Exception in thread "main" java.lang.IllegalAccessException: Class Singleton.Test2 can not access a member of class Singleton.HungrySingleton with modifiers "private"
        //	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
        // 加入下面代码，就相当于把private 权限给打开了。
        constructor.setAccessible(true);
        HungrySingleton instance = HungrySingleton.getInstance();
        //利用对象构造器，反射机制，重新new 一份对象。相对于这个对象是复制的
        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);
*/


        //3. 序列化测试方法（枚举类）, 枚举类不会被序列化 和反序列化 破坏。
 /*       EnumInstance instance = EnumInstance.getInstance();
        instance.setData(new Object());

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton_file"));
        //把 instance 实例 写入 oos 这个流。
        oos.writeObject(instance);

        //读取 上面oos 写入的 流。
        File file = new File("Singleton_file");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

        EnumInstance newInstance = (EnumInstance) ois.readObject();

        System.out.println(instance.getData());
        System.out.println(newInstance.getData());
        System.out.println(instance.getData() == newInstance.getData());*/
        /**
         * 结果：
         *      java.lang.Object@568db2f2
         *      java.lang.Object@568db2f2
         *      true
         **/

        //4. 防止反射攻击（枚举类）
        // 因为构造器是私有的 无法被new出来；；但是：我们能不能通过反射把构造器的权限打开，然后去获取这个对象。
        //Class objectClass = HungrySingleton.class;

        Class objectClass = EnumInstance.class;

        //Constructor 是反射里边的。
        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);

        constructor.setAccessible(true);

        EnumInstance instance = EnumInstance.getInstance();
        //利用对象构造器，反射机制，重新new 一份对象。相对于这个对象是复制的
        EnumInstance newInstance = (EnumInstance) constructor.newInstance("cxcx",666);

        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);

        //枚举类的 应用。
        EnumInstance enumInstance = EnumInstance.getInstance();
        enumInstance.printTest();
    }
}
